/*
	Script: pax
		This is the main PAX library, encapsulating xmlHttpRequest, event handeling, and so on.
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

*/


/*	Property: pax
	Main library OBJ	*/
var pax = pax || {};


/*	Method: pax.$
	Simple alias to document.getElementById
	
	Parameters:
		id - The id of the DOM element

	Example:
		(start code)
			<div id='myElement'>This is my Elements innerHTML</div>
			[:.
				var ele = pax.$('myElement');
				alert( ele.innerHTML );
			:]
		(end)
		Assigns a pointer to *ele*, and the alerts the contents.
*/
pax.$ = function( id ) {
	return (typeof( id ) == 'string' )? document.getElementById( id ): id;
};

var $ = $ || pax.$;	//	Assign $ to our function, unless it already exists - play nicely with other libraries



/*	--- Start Query ---	*/

/*	Behave like jQuery.

	NOTE: 	The point of this is NOT to build a jQuery clone, rather it is to provide 
			the most often used jQuery functionality. We override the sizzle functionality 
			to return a chainable object from pax.query
	
	TODO: Find out what the most often used jQuery functions are. Assume for now:
	
	[done]	. Chainable events
	[done]	. find(...)
	. DOM manipulation, eg: css, attr, etc...
	. Effects
	
	We don't want / need full jQuery - people will include and use jQuery for that :o)
*/

/*	Method: pax.query
    Emulates the most common jQuery functionality such as chainable events and find.
	
	Parameters:
		id - The id of the DOM element
*/

var jQuery = jQuery || null;

if( jQuery && jQuery.prototype ) {
	//	If we detect jQuery, pax.query should = jQuery.
	pax.query = function(){};
	pax.query = pax.query.prototype = jQuery;
} else {
	//	Create a mini set of jQuery-like functionality, apologies to J.Resig for
	//	creating bastardised functions that work similarly to his library :oP
	
	//	Setup Sizzle selector engine
	pax.sizzle = Sizzle;

	pax.query = function( queryStr, scope ) {
		return new pax.query.prototype.init( queryStr );
	};

	//	Create the query function for chaining
	pax.query.prototype = {
		init: function( queryStr, scope ) {
			this.results = pax.sizzle( queryStr, scope );
			return this;
		},
		length: 0,
		size: function() { return this.length; },
		get: function( index ) {
			return (index && index != 0)? this.result: this.result[index];
		},
		find: function( queryStr ) {
			var found = [];
			for( var i = 0; i < this.results.length; i++ ) {
				var matches = pax.sizzle.find( queryStr, this.results[i] )['set'] || [];
				for( var m = 0; m < matches.length; m++ ) {
					found.push( matches[m] );
				}
			}
			this.results = found;
			return this;
		},
		val: function( value ) {
			return pax.form.getFieldValue( results );
		},
		attr: function() {
			//	Use pax.util.attrib, which needs some refactoring...
		},
		createBindFunc: function( eventName ) {
			return function( func ) {
				for( var i = 0; i < this.results.length; i++ ) {
					pax.event.bind( this.results[i], eventName, func );
				}
				return this;
			};
		},
		show: function( speed, callback ) {
			//	TODO: use the pax.fx fadein or perhaps reveal function.
			return this;
		}
	};

	//	Prototypify (expose) the init function
	pax.query.prototype.init.prototype = pax.query.prototype;

	//	Create event handlers for chainablilty
	var paxQueryEvents = [
		'blur', 'focus', 'load', 'resize', 'scroll', 'unload', 'click', 'dblclick', 
		'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'change', 
		'select', 'submit', 'keydown', 'keypress', 'keyup', 'error' 
	];

	for( var i = 0; i < paxQueryEvents.length; i++ ) {
		var eventName = paxQueryEvents[i];
		var myFunc = pax.query.prototype.createBindFunc( eventName );
		pax.query[eventName] = pax.query.prototype[eventName] = myFunc;
	};

}

/*	--- End Query ---	*/



/*	Property: pax.docRoot
	String to prefix all server calls with, useful for ensuring security is not broken easily */
pax.docRoot = '';
/*	Property: pax.requestQueue
	Array of requests.	*/
pax.requestQueue = [];
/*	Property: pax.showStatusSpinner
	Boolean to choose if we should display the status spinner	*/
pax.showStatusSpinner = true;
/*	Property: pax.statusSpinnerContainer
	The default container (DOM Element) to put the statusbox spinner into.	*/
pax.statusSpinnerContainer = 'StatusBox';
/*	Property: pax.bindQueue
	List of functions bound to objects. {obj:object, event:event, func:function}	*/
pax.bindQueue = [];
/*	Property: pax.statusSpinnerClass
	The default CSS class to use for the spinner	*/
pax.statusSpinnerClass = 'narrowDark';
/*	Property: pax.idCounter
	Unique ID counter for generating temporary DOM IDs	*/
pax.idCounter = 0;

/*	Property: pax.showDebug
	Outputs debug to console in certain instances, if true	*/
pax.showDebug = false;

pax.event = pax.event || {};							// Events - Browser events library


/*	Simple workaround for browsers without firebug
	See http://www.getfirebug.com/
*/
if( !( 'console' in window ) || !( 'firebug' in console ) ) {
	var names = [
		'log', 'debug', 'info', 'warn', 'error', 'assert', 'dir', 'dirxml',
		'group', 'groupEnd', 'time', 'timeEnd', 'count', 'trace', 'profile', 'profileEnd'
	];

	window.console = {};
	for( var i = 0; i < names.length; ++i ) {
		window.console[names[i]] = function(){};
	}
		
	window.console.log = function( message ) {
		var debugBox = pax.$( 'console_output' );
		if( debugBox ) {
			debugBox.value = message + '\n' + debugBox.value;
		}
		window.status = message;
	};
}


//	Error function; all functions and widgets should use this to report CRITICAL errors.
//	Obviously we don't always want to alert the error, so this *could* be overridden before using PAX in production
pax.criticalError = function( err ) {
	alert( err );
};


/*	Method: pax.post
	Send a POST request via the XMLHttpRequest object. Note that you do not need to specify all parameters

	Parameters:
		url - URL to send a request to.
		post - POST string to include in the call, use <pax.postString> to encode an object containing key / value pairs
		callBack - Function to call once the request responds.
		callDesc - Description of what the request is doing - is displayed as a title for the status spinner.
		spinnerBox - DOM element to put the spinners into
		spinnerClass - Class to use on the request spinner.

	Note that a callback function needs 3 parameters:
		xml	- If the response is in XML format, this will contain the response, otherwise it will be empty
		txt - If the response is in Text format, this will contain the response, otherwise it will be empty
		url - what URL the response came from

	Example:
		(start code)
			<div id='pax.post.example1'></div>
			[:.
				var mycallBack = function( xml, txt, url ) {
					var response = pax.unJSON( txt );
					pax.$('pax.post.example1').innerHTML = "The name retreived from the server is: " + response.data[0].name;
				};
				pax.post( '/pax/pax.post.example.php', pax.postString( { action: 'editAction', id: 2 } ), mycallBack, 'Post example', 'pax.post.example1' );
			:]
		(end)

		This will send a request, with id = 2, action = 'editAction', showing the default spinner image via the spinner queue.
		It should be noted that the php script has a 2 second delay, so that you actually see the spinner.
		
	Example:
		(start code)
			<div id='pax.post.example2'></div>
			[:.
				var mycallBack = function( xml, txt, url ) {
					var response = pax.unJSON( txt );
					pax.$('pax.post.example2').innerHTML = "The country retreived from the server is: " + response.data[0].country;
				};
				pax.post( '/pax/pax.post.example.php', pax.postString( { action: 'editAction', id: 1 } ), mycallBack, 'Post example', 'pax.post.example2', 'load_snake_blue.gif' );
			:]
		(end)

		This will send a request, with id = 1, action = 'editAction', showing a custom spinner image via the spinner queue.
		It should be noted that the php script has a 2 second delay, so that you actually see the spinner.
*/
pax.post = function( url, post, callBack, callDesc, spinnerBox, spinnerClass ) {
	post = ( typeof( post ) == 'string' )? post : pax.postString( post );
	var rqObj = pax.setCallBack( url, callBack, callDesc, spinnerBox, spinnerClass );
	rqObj.ro.open( 'POST', pax.docRoot + url, true );
	rqObj.ro.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
	rqObj.ro.send( post );
	
	pax.showStatus( spinnerClass );
	
	return rqObj;
};


/*	Method: pax.get
	Send a GET request via the XMLHttpRequest object. Note that you do not need to specify all parameters

	Parameters:
		url - URL to send a request to.
		callBack - Function to call once the request responds.
		callDesc - Description of what the request is doing - is displayed as a title for the status spinner.
		spinnerBox - DOM element to put the spinners into
		spinnerClass - Class to use on request spinner box.

	Note that a callback function needs 3 parameters:
		xml	- If the response is in XML format, this will contain the response, otherwise it will be empty
		txt - If the response is in Text format, this will contain the response, otherwise it will be empty
		url - what URL the response came from

	Example:
		(start code)
			<fieldset><legend>Response from pax.get</legend>
				<div id='pax.get.example1'></div>
			</fieldset>
			[:.
				var url = '/pax/pax.get.example1.txt';
				
				var callBack = function( xml, txt, url ) {
					pax.$('pax.get.example1').innerHTML = txt;
				};
				
				pax.get( url, callBack, 'get example', false, false );
			:]
		(end)
		
	Note:
		You can add a timestamp at the end of the URL, to make it unique, to avoid caching issues in certain browsers

*/
pax.get = function( url, callBack, callDesc, spinnerBox, spinnerClass ) {
	var rqObj = pax.setCallBack( url, callBack, callDesc, spinnerBox, spinnerClass );
	rqObj.ro.open( 'GET', pax.docRoot + url, true );
	rqObj.ro.send( null );
	
	pax.showStatus( spinnerClass );
	
	return rqObj;
};


/*	Method: pax.getId
	Returns the current unique ID
	
	Parameters:
		none
		
	Example:
		(start code)
		(end)
*/
pax.getId = function() {
	return '_pax_' + pax.idCounter;
};


/*	Method: pax.getNextId
	Increments the unique ID counter, and returns the ID
	
	Parameters:
		none
		
	Example:
		(start code)
		(end)
*/
pax.getNextId = function() {
	pax.idCounter += 1;
	return pax.getId();
};


/*	Method: pax.event.bind
	Add an event to an OBJ
	
	Parameters:
		object - DOM Object to attach the event / function to
		event - The event we're listening for. Note that we don't add the 'on' part
		func - Function to run when event fires

	Example:
		(start code)
			<div id="pax.event.bind.example1">Click me!</div>
			[:.
				pax.event.bind( pax.$('pax.event.bind.example1'), 'click', function() { alert('Thanks for clicking!') } );
			:]
		(end)

		Alerts when the div's onclick function fires.
		
	Notes:
		- To avoid memory leaks, the bind function automatically adds an <pax.event.unbindAll> function call to window.onunload
		- It is best practice to NOT to assign local closure variables to an outside reference.
		
	If you use:
		
			var el = document.getElementById('exampleWindowCloseButton');
			pax.event.bind( el, 'click', function() {...
			
	Then the memory leak will occur. If you use:
		
			pax.event.bind( pax.$('exampleWindowCloseButton'), 'click', function() {...
			
	Then there is no memory leak, as pax.unbindAll will clear the binding before we unload the window.

*/
pax.event.bind = function( obj, event, func ) {
	if( typeof( func ) == 'undefined' || typeof( event ) == 'undefined' || typeof( obj ) == 'undefined' || obj == null )return false;
	
	if( obj.attachEvent ) {
		obj['e' + event + func] = func;
		obj[event + func] = function(){ obj['e' + event + func]( window.event ); };
		obj.attachEvent( 'on' + event, obj[event + func] );
	} else obj.addEventListener( event, func, false );
	//	Check if the binding exists in the queue before pushing it.
	if( ! pax.event.hasBinding( obj, event, func ) ) {
		pax.bindQueue.push( { obj: obj, event: event, func: func } );
		var boundObj = pax.bindQueue[ pax.bindQueue.length -1 ];
	}
	
	if( pax.bindQueue.length == 1 )pax.event.bind( window, 'unload', pax.event.unbindAll );
	
	return boundObj;
};

//	Same as bind event, but binds a list of events to the same function
pax.event.bindEventList = function( obj, event, func ) {
	var eventList = ( typeof( event ) == typeof( [] ) )? event: [event];
	var boundObjs = [];
	for( var i = 0; i < eventList.length; i++ ) {
		boundObjs.push( pax.event.bind( obj, eventList[i], func ) );
	}
	return boundObjs;
};


/*	Method: pax.event.bindOne
	Binds the event once only, ie: clears any other bound functions on the event first.
*/
pax.event.bindOne = function( obj, event, func ) {
	if( pax.util.getType( event ) == 'array' ) {
		for( var i in event ) {
			if( pax.event.hasBinding( obj, event[i] ) ) {
				pax.event.unbind( obj, event[i] );
			}
		}
		return pax.event.bindEventList( obj, event, func );
	} else {
		if( pax.event.hasBinding( obj, event ) ) {
			pax.event.unbind( obj, event );
		}
		return pax.event.bind( obj, event, func );
	}
};


/*	Method: pax.event.hasBinding
	Returns true if an object has a certain event bound to the given function. This uses the <pax.bindQueue>
	
	Parameters:
		object - DOM Object we're inspecting
		event - The event we're testing for. Note that we don't add the 'on' part
		func - Function that we're testing for

	Example:
		(start code)
			<div id="pax.event.hasBinding.example1"></div>			
			[:.
				var myFunc = function() {
					alert('Thanks for clicking!');
				};
				var box = pax.$('pax.event.hasBinding.example1');
				box.innerHTML += 'has binding: ' + pax.event.hasBinding( box, 'click', myFunc ) + '<bR>';
				box.innerHTML += 'adding binding...<bR>';
				pax.event.bind( box, 'click', myFunc );
				box.innerHTML += 'has binding: ' + pax.event.hasBinding( box, 'click', myFunc ) + '<bR>';
				box.innerHTML += 'removing binding...<bR>';
				pax.event.unbind( box, 'click', myFunc );
				box.innerHTML += 'has binding: ' + pax.event.hasBinding( box, 'click', myFunc ) + '<bR>';
			:]
		(end)

		
*/
pax.event.hasBinding = function( obj, event, func ) {
	var result = false;
	for( var x = 0; x < pax.bindQueue.length; x++ ) {
		if( typeof( func ) != 'undefined' ) {
			if( ( ( pax.bindQueue[x].obj == obj ) && ( pax.bindQueue[x].event == event ) && ( pax.bindQueue[x].func == func ) ) ) {
				result = true;
			}
		} else {
			if( ( ( pax.bindQueue[x].obj == obj ) && ( pax.bindQueue[x].event == event ) ) ) {
				result = true;
			}
		}
	}
	return result;
};


/*	Method: pax.event.unbind
	Remove an event from an OBJ
	
	Parameters:
		object - DOM Object to remove the event from
		event - The event we assigned originally. Note that we don't add the 'on' part
		func - Function that was assigned to run when event fires

	Example:
		(start code)
			<div id="pax.event.unbind.example1">Click me!</div>
			[:.
				var cluck = function() {
					pax.event.unbind( pax.$('pax.event.unbind.example1'), 'click', cluck );
					alert('Thanks for clicking! Now click OK, and try again. It wont work the 2nd time, as we have unbound the function.');
				};
				pax.event.bind( pax.$('pax.event.unbind.example1'), 'click', cluck );
			:]
		(end)
		Removes the previously bound function event.

	Example:
		(start code)
			<div id="pax.event.unbind.example2">Click me!</div><input type='button' id="pax.event.unbind.example2.button" value='Unbind'>
			[:.
				var cluck1 = function() { console.log('1'); };
				var cluck2 = function() { console.log('2'); };
				pax.event.bind( pax.$('pax.event.unbind.example2'), 'click', cluck1 );
				var unbindCluck = function() {
					pax.event.unbind( pax.$('pax.event.unbind.example2'), 'click' );
					alert('Thanks for clicking! Now click OK, and try again. It wont work the 2nd time, as we have unbound the function.');
				};
				pax.event.bind( pax.$('pax.event.unbind.example2'), 'click', cluck2 );
				pax.event.bind( pax.$('pax.event.unbind.example2.button'), 'click', unbindCluck );
			:]
		(end)
		Removes all previously bound click events.
*/
pax.event.unbind = function( obj, event, func ) {
	if( func ) {
		if (obj) {
			if (obj.removeEventListener) {
				obj.removeEventListener(event, func, false);
			}
			else 
				if (obj.detachEvent) {
					obj.detachEvent('on' + event, obj[event + func]);
					obj[event + func] = null;
				}
			
			//	Remove item from queue
			var newBindQueue = [];
			for (var x = 0; x < pax.bindQueue.length; x++) {
				if (!((pax.bindQueue[x].obj === obj) && (pax.bindQueue[x].event === event) && (pax.bindQueue[x].func === func))) {
					newBindQueue.push(pax.bindQueue[x]);
				}
			}
			pax.bindQueue = newBindQueue;
		}
	} else {
		if (obj) {
			//	Assume we're detatching ALL functions for the given event on the obj
			for (var i = 0; i < pax.bindQueue.length; i++) {
				if (event) {
					if (pax.bindQueue[i].obj == obj && pax.bindQueue[i].event == event) {
						pax.event.unbind(pax.bindQueue[i].obj, pax.bindQueue[i].event, pax.bindQueue[i].func);
					}
				}
				else {
					if (pax.bindQueue[i].obj == obj) {
						pax.event.unbind(pax.bindQueue[i].obj, pax.bindQueue[i].event, pax.bindQueue[i].func);
					}
				}
			}
		}
	}
};


/*	Method: pax.event.unbindAll
	Remove all registered events from all OBJs. This method is automatiacally called on window unload.
	
	Note:
		There is no example, as it would break the example window's functionality. It removes all previously bound function events.
*/
pax.event.unbindAll = function( unbindObj ) {
	if( typeof( unbindObj ) != 'undefined' ) {
		for( var i = 0; i < pax.bindQueue.length; i++ ) {
			with( pax.bindQueue[i] ) {
				if( obj == unbindObj )pax.event.unbind( obj, event, func );
			}
		}
	} else {
		for( var i = 0; i < pax.bindQueue.length; i++ ) {
			with( pax.bindQueue[i] ) {
				pax.event.unbind( obj, event, func );
			}
		}
		pax.bindQueue = [];
	}
};



/*	Method: pax.event.bindKeyDown
	Binds a key down event to an object
	
	Parameters:
		object - DOM Object to bind the keydown event to
		func - Function that runs when the key fires

	Example:
		(start code)
			Type in this box: <input type="text" id="pax.event.bindKeyDown.keyInput"> Key code: <input type="text" id="pax.event.bindKeyDown.keyPressed" size="3">
			[:.
				var myKeyFunc = function( event ) {
					pax.$('pax.event.bindKeyDown.keyPressed').value = event.keyCode;
				};
				pax.event.bindKeyDown( pax.$('pax.event.bindKeyDown.keyInput'), myKeyFunc );
			:]
		(end)

		This will show what keycode is returned when you press a key in the first text box
*/
pax.event.bindKeyDown = function( obj, func ) {
	// This may fail! - should check for safari differently.
	if( pax.isSafari )return pax.event.bind( obj, 'keypress', func );
	else return pax.event.bind( obj, 'keydown', func );
};


/*	Method: pax.event.bindKeyUp
	Binds a key up event to an object
	
	Parameters:
		object - DOM Object to bind the keyup event to
		func - Function that runs when the event fires

	Example:
		(start code)
			Type in this box: <input type="text" id="pax.event.bindKeyUp.keyInput"> Key code: <input type="text" id="pax.event.bindKeyUp.keyPressed" size="3">
			[:.
				var myKeyFunc = function( event ) {
					pax.$('pax.event.bindKeyUp.keyPressed').value = event.keyCode;
				};
				pax.event.bindKeyUp( pax.$('pax.event.bindKeyUp.keyInput'), myKeyFunc );
			:]
		(end)

		This will show what keycode is returned when you press a key, then let go of it, in the first text box
*/
pax.event.bindKeyUp = function( obj, func ) {
	if( pax.isSafari )return pax.event.bind( obj, 'keypress', func );
	else return pax.event.bind( obj, 'keyup', func );
};


/*	Method: pax.event.bindKeyPress
	Binds a key press event to an object. The reason we have this method is that it will work in *all* browsers
	
	Parameters:
		object - DOM Object to bind the keypress event to
		func - Function that runs when the event fires

	Example:
		(start code)
			Type in this box: <input type="text" id="pax.event.bindKeyPress.keyInput"> 
			Key code: <input type="text" id="pax.event.bindKeyPress.keyPressed" size="3">
			[:.
				var myKeyFunc = function( event ) {
					pax.$('pax.event.bindKeyPress.keyPressed').value = event.keyCode;
				};
				pax.event.bindKeyPress( pax.$('pax.event.bindKeyPress.keyInput'), myKeyFunc );
			:]
		(end)

		This will show what keycode is returned when you press a key in the first text box
*/
pax.event.bindKeyPress = function( obj, action ) {
	pax.event.bind( obj, 'keypress', action );
};
  

/*	Method: pax.scope
	Brings the scope of an object into the current scope, with the optional given name.
	If no name is specified, then it is assumed an object with 'name' -> value is passed,
	and each variable will be available as 'name'. The global variable is simply a copy, and is not linked to the local variable
	
	Parameters:
		obj - object to put into global name space
		name - optional name to use for the object; if no name is given, the object will be traversed, and the items in it will be globalised.

	Example:
		(start code)
			Type before global scope: <div id='pax.scope.example1.type' style='border: 1px solid red'></div>
			Type after global scope: <div id='pax.scope.example1.typeAfter' style='border: 1px solid green'></div>
			[:.
				var globalise = function () {
					var localVariable = "local";
					pax.scope( localVariable, 'gVar' );
				};
				pax.$('pax.scope.example1.type').innerHTML = typeof(gVar) + '<b' + 'r>';
				globalise();
				pax.$('pax.scope.example1.typeAfter').innerHTML = typeof(gVar) + '<b' + 'r>';
			:]
		(end)
		This example brings a variable from the scope of the function, into the current scope.

	Example:
		(start code)
			Log: <div id='pax.scope.example1.log' style='border: 1px solid black'></div>
			[:.
				var myFunc = {
					init: function ( value ) {
						this.variable = value;
					},
					scopalise: function() {
						pax.scope( this.variable, 'gvar2' );
					},
					log: function( header) {
						var t = pax.$('pax.scope.example1.log');
						t.innerHTML += '[' + header + ']<b' + 'r> local type: <b>' + typeof(this.variable) + '</b> value: <b>' + 
							this.variable + '</b><b' + 'r>global type: <b>' + typeof(gvar2) + '</b>';
						if( typeof(gvar2) != 'undefined' )t.innerHTML += ' value: <b>' + gvar2 + '</b>';
						t.innerHTML += '<hr>';
					}
				};
				myFunc.init( 'I\'m a local' );
				myFunc.log( 'before scopalise' );
				myFunc.scopalise();
				myFunc.log( 'after scopalise' );
				gvar2 = "I\'m a global";
				myFunc.log( 'change scopalised variable' );
			:]
		(end)
		This example brings a variable from the scope of the function, into the current scope, and shows that it is just a copy, not the actual variable.
		
		Note: This function is REALLY expensive due to eval!
*/
pax.scope = function( obj, name ) {
	var myObj = obj;
	
	if( pax.util.getType( obj ) != 'object' ) {
		if( typeof( name ) != 'string' ) return false;	//	must specify a name, if you don't specify an object, otherwise you won't know how to reference it.
		myObj = { name: obj };
	} else {
		if( typeof( name ) != 'string' )name = 'global';
	}

	myObj[name] = obj;
	
	window['__' + name + '__scope'] = myObj;
	
	//	This may not work in opera or safari!
	for( var v in myObj ) {
		var evalLine = ["var ", v, " = (window['__", name, "__scope'])? window['__", name, "__scope'].", v, ": null"].join('');
		if( window.execScript ) {
			try {
				window.execScript( evalLine );
			}
			catch( e ) {
				//	If we cannot execScript it 
				window.eval( evalLine );
			}
		} else if( window.eval ) {
			window.eval( evalLine );
		}
		else eval( evalLine );
	}
	
	//	Cleanup the window variable here...
	window['__' + name + '__scope'] = null;
};


//	Prevent propagation of an event
pax.event.preventpropagate = function(e) {
	if( typeof( e.stopPropagation ) != 'undefined' )e.stopPropagation();
	else e.cancelBubble = true;
};


/*	Method: pax.unJSON
		Converts a JSON string to an object. This method is only here because it is best practice to do it this way.

	Parameters:
		jsonStr - the string to convert.

	Example:
		(start code)
			<div id='pax.unJSON.example1'></div>
			[:.
				var myFruitString = "{	'apples': ['Granny Smith', 'Red delicious'], ";
				myFruitString += "		'oranges': ['Valencia', 'Blood'], ";
				myFruitString += "		'pears': 'green' }";
				var myFruits = pax.unJSON( myFruitString );
				pax.$('pax.unJSON.example1').innerHTML = pax.util.toString(myFruits);
			:]
		(end)
		Will return an object with this shape:
		
		{ 
			'apples': [
				'Granny Smith',
				'Red delicious'
			],
			'oranges': [
				'Valencia',
				'Blood'
			],
			'pears': 'green'
		}

*/
pax.unJSON = function( jsonStr ) {
	return eval( '(' + jsonStr + ')' );
};


/*	Method: pax.JSON
		Converts an object into a JSON string.

	Parameters:
		obj - the object to convert.

	Example:
		(start code)
			<div id='pax.JSON.example1'></div>
			[:.
				var myFruitObj = { 
					'apples': ['Granny Smith', 'Red delicious'], 
					'oranges': ['Valencia', 'Blood'],
					'pears': 'green' 
				};
				var myFruitString = pax.JSON( myFruitObj );
				pax.$('pax.JSON.example1').innerHTML = myFruitString;
			:]
		(end)
		Will display the object in JSON string format, with strings URI encoded
*/
pax.JSON = function( obj, prev ) {
	prev = prev || [];
	//	This will show '*circular ref*', when we have string values repeated.
	//	TODO: create a JSONP-like obj instead.
	if( pax.util.hasValue( obj, prev ) && pax.util.getType( obj ) == 'object' ) {
		return '"*circular ref*: ' + obj + '"';
	} else {
		var result = [];
		prev.push( obj );
		var type = pax.util.getType(obj);
		switch( type ) {
			case 'string':
				return '"' + encodeURIComponent( obj.replace( new RegExp('(["\\\\])', 'g'), '\\$1') ).replace( /%20/g, '+' ) + '"';
			case 'array':
				for( var i = 0; i < obj.length; i++ )result.push( pax.JSON( obj[i], prev ) );
				return '[' + result.join(',') + ']';
			case 'object':
				for (var property in obj)result.push( '"' + property + '":' + pax.JSON( obj[property], prev ) );
				return '{' + result.join(',') + '}';
		}
	}
	return String( obj );
};


/*	Method: pax.postString
	URL encodes an oject into a post string to be used in <pax.post>.
	
	Parameters:
		param - an object containing key / value pairs to be encoded

	Example:
		(start code)
			Before: <div id='pax.postString.example1.before'></div>
			After: <div id='pax.postString.example1.after'></div>
			[:.
				var postObj = { id: 23, action: 'update' };
				var myPost = pax.postString( postObj );
				pax.$('pax.postString.example1.before').innerHTML = pax.util.inspect(postObj);
				pax.$('pax.postString.example1.after').innerHTML = myPost;
			:]
		(end)

		When this is run, myPost becomes '&id=23&action=update'
*/
pax.postString = function( param ) {
	var url = "";
	var count = 0;
	
	for( var p in param ) {
		//	We use the built in URI encoder here...
		url += "&" + p + "=" + encodeURIComponent( param[p] ).replace( /%20/g, '+' );
	}

	return url;
};


//	WIP: The above does not add the '?', fix it now!
pax.newPostString = function( param ) {
	var str = "";
	var count = 0;
	
	for( var p in param ) {
		//	We use the built in URI encoder here...
		str += ( (count ==0)? "?": "&" ) + p + "=" + encodeURIComponent( param[p] ).replace( /%20/g, '+' );
		count += 1;
	}

	return str;
};


/*	Method: pax.postParams
	URL decodes a post string back into a set of parameters
	
	Parameters:
		string - a string to be decoded into parameters
*/
pax.postParams = function( string ) {
	var queryString = {};
	var parameters = string.split('&');
	for( var i = 0; i < parameters.length; i++ ) {
		var pos = parameters[i].indexOf('=');
		if( pos > -1 ) {
			var paramname = parameters[i].substring( 0, pos );
			var paramval = parameters[i].substring( pos + 1 );
			queryString[paramname] = unescape( paramval.replace(/\+/g,' ') );
		} else {
			//special value when there is a querystring parameter with no value
			queryString[parameters[i]] = "";
		}
	}		
	return queryString;
};


/*	Method: pax.defaultArgs
	Overrides default arguments in the given object only if they exist in the given argument
	
	Parameters:
		obj - an object containing key / value pairs to be overridden
		args - an object containing optional key / value pairs to override the contents of obj

	Example:
		(start code)
			Before: <div id='pax.defaultArgs.example1.before'></div>
			After: <div id='pax.defaultArgs.example1.after'></div>
			[:.
				var config = { name: 'Bob Smith', email: 'bob@smith.com', action: 'update' };
				pax.$('pax.defaultArgs.example1.before').innerHTML = pax.util.toString( config );
				pax.defaultArgs( config, { action: 'insert' } );
				pax.$('pax.defaultArgs.example1.after').innerHTML = pax.util.toString( config );
			:]
		(end)
		Changes config to contain the action 'insert'
*/
pax.defaultArgs = function( obj, args ) {
	//	Loop on supplied args
	for( var a in args ) {
		for( var o in obj ) {
			//	Match on default obj keys, and override with args
			if( o == a )obj[o] = args[a];
		}
	}
	
	return obj;
};


/*	Method: pax.defAddArgs
	Overrides default arguments in the given object, and adds any other attributes in the argument
	
	Parameters:
		obj - an object containing key / value pairs to be overridden
		args - an object containing optional key / value pairs to override the contents of obj

	Example:
		(start code)
			Before: <div id='pax.defaultArgs.example1.before'></div>
			After: <div id='pax.defaultArgs.example1.after'></div>
			[:.
				var config = { name: 'Bob Smith', email: 'bob@smith.com', action: 'update' };
				pax.$('pax.defaultArgs.example1.before').innerHTML = pax.util.toString( config );
				pax.defaultArgs( config, { action: 'insert' } );
				pax.$('pax.defaultArgs.example1.after').innerHTML = pax.util.toString( config );
			:]
		(end)
		Changes config to contain the action 'insert'
*/
pax.defAddArgs = function( obj, args ) {
	//	Loop on supplied args
	for( var a in args ) {
		obj[a] = args[a];
	}
	
	return obj;
};





/*	Private Method: pax.getRequestObject
	Creates a XMLHttpRequest object. In the context of the PAX library, this is a private method, although you could use it to create a request object yourself.
	
	Parameters:
		none

	Example:
		(start code)
			<fieldset><legend>Response from Request Object</legend>
				<div id='pax.getRequestObject.example1'></div>
			</fieldset>
			[:.
				var myRO = pax.getRequestObject();
				
				var callBack = function( xml, txt, url ) {
					pax.$('pax.getRequestObject.example1').innerHTML = txt;
				};
				
				myRO.onreadystatechange = function() {
					if( myRO.readyState == 4 ) {
						callBack( myRO.responseXML, myRO.responseText, '' );
					}
				};
	
				myRO.open( 'GET', '/pax/pax.getRO.example1.txt', true );
				myRO.send( null );
				
			:]
		(end)

		Creates a request object, and uses it to load some text from the server. You don't need to do this, you should use wither <pax.get> or <pax.post>
*/
pax.getRequestObject = function() {
	var ro;
	
	/*
	if( typeof XMLHttpRequest != undefined ) {
		ro = new XMLHttpRequest();
	} else {
		try {
			ro = new ActiveXObject("Microsoft.XMLHTTP");
		}
		catch( e ) {
			//ro = new XMLHttpRequest();
			//	Fallback to IE 4?
		}
	}
	*/
	
	try {
		ro = new ActiveXObject("Microsoft.XMLHTTP");
	}
	catch( e ) {
		//ro = new XMLHttpRequest();
		//	Fallback to IE 4?
		if (typeof XMLHttpRequest != undefined) {
			ro = new XMLHttpRequest();
		}
		else {
			// We are SOL.
		}
	}
	
	return ro;
};


/*	Private Method: pax.removeRequestQueueItem
	Removes the specified request object from the request queue. This is a private method.
	
	Parameters:
		robj - Request object to remove

*/
pax.removeRequestQueueItem = function( robj ) {
	var newRequests = [];
	for( var x = 0; x < pax.requestQueue.length; x++ ) {
		if( ! ( robj === pax.requestQueue[x].ro ) ) {
			newRequests.push( pax.requestQueue[x] );
		}
	}
	pax.requestQueue = newRequests;
};


/*	Private Method: pax.getRequestQueueItem
	Retreives the specified request object from the request queue. This is a private method.
	
	Parameters:
		robj - Request object to find

*/
pax.getRequestQueueItem = function( robj ) {
	for( var x = 0; x < pax.requestQueue.length; x++ ) {
		if( robj === pax.requestQueue[x].ro ) {
			return pax.requestQueue[x];
		}
	}
	return null;
};


/*	Private Method: pax.showStatus
	Display the status for each request in the queue, using a spinner image: assumes a container with id of <pax.statusSpinnerContainer>, which will be created if it doesn't exist.
	This is a private method, although you could use it to display an image for each request in the queue.
	
	Parameters:
		spinnerClass - Image to use
*/
pax.showStatus = function( spinnerClass ) {

	spinnerClass = (spinnerClass)? spinnerClass: pax.statusSpinnerClass;

	if( pax.showStatusSpinner ) {
		var sb = document.getElementById( pax.statusSpinnerContainer );

		// Insert the status box, if it doesn't already exist
		//	Note: this will appear at the end of the body, that may not be the best place...
		if( sb == null ) {
			var div = document.createElement('DIV');
			div.id = pax.statusSpinnerContainer;
			div.style.position = 'absolute';
			document['body'].appendChild( div );
			sb = document.getElementById( div.id )
		}
		
		sb.innerHTML = '';
		pax.util.addClassName( sb, spinnerClass );
		for( var re in pax.requestQueue ) {
			if( typeof pax.requestQueue[re].spinnerBox != 'undefined' ) {
				sb = document.getElementById( pax.requestQueue[re].spinnerBox );
				if( sb )pax.util.addClassName( sb, spinnerClass );
			}
		}
	}
};


/*	Method: pax.hideStatus
	Hides the status spinner
	
	Parameters:
		none

*/
pax.hideStatus = function() {
	if( pax.showStatusSpinner ) {
		var sb = document.getElementById( pax.statusSpinnerContainer );
		if( sb ) {
			var spinnerClass = pax.statusSpinnerClass;
			pax.util.removeClassName( sb, spinnerClass );
			
			for( var re in pax.requestQueue ) {
				spinnerClass = (pax.requestQueue[re].spinnerClass)? pax.requestQueue[re].spinnerClass: pax.statusSpinnerClass;
				if( typeof pax.requestQueue[re].spinnerBox != 'undefined' )sb = document.getElementById( pax.requestQueue[re].spinnerBox );
				if( sb )pax.util.removeClassName( sb, spinnerClass );
			}
		}
	}
};


/*	Method: pax.setCallBack
	Set the onreadystatechange to a callback function, which calls the given function. Also shows hides the status spinner as required.

	Parameters:
		url - URL to send a request to.
		callBack - Function to call once the request responds.
		callDesc - Description of what the request is doing - is displayed as a title for the status spinner.
		spinnerBox - DOM element to put the spinners into, false = don't use
		spinnerClass - CSS Class to use for the request spinner, false = don't use

	Example:
		This method is only useful if you're overriding one of the request methods, such as <pax.get> or <pax.post>
		(start code)
			<fieldset><legend>Response from Request Object</legend>
				<div id='pax.setCallBack.example1'></div>
			</fieldset>
			[:.
				var url = '/pax/pax.getRO.example1.txt';
				
				var callBack = function( xml, txt, url ) {
					pax.$('pax.setCallBack.example1').innerHTML = txt;
				};
				
				var myRObj = pax.setCallBack( url, callBack, 'setCallBack example', false, false );
				myRObj.ro.open( 'GET', url, true );
				myRObj.ro.send( null );
			:]
		(end)

		This will send a request, and receive a response via using the setCallBack method.
*/
pax.setCallBack = function( url, callBack, callDesc, spinnerBox, spinnerClass ) {
	spinnerBox = ( typeof spinnerBox != 'undefined' )? spinnerBox: pax.statusSpinnerContainer;
	spinnerClass = ( typeof spinnerClass != 'undefined' )? spinnerClass: pax.statusSpinnerClass;
	
	var robj = pax.getRequestObject();

	var onReadyStateChange = function() {
		if( robj.readyState == 4 ) {
			var resXML = robj.responseXML;
			var resTXT = robj.responseText;
			pax.cancel( robj );
			callBack( resXML, resTXT, url);
		}
		//	We can add functions to handle other states here...
	};
	
	var roInterval = setInterval( onReadyStateChange, 12 );
	
	//	IE requires that we wrap the XMLHttpRequest in an object, as it doesn't allow expandos on the request object.
	var rqObj = { 
		ro: robj, 
		url: url, 
		callBack: callBack, 
		callDesc: callDesc, 
		spinnerBox: spinnerBox, 
		spinnerClass: spinnerClass,
		timerInterval: roInterval		
	};

	pax.requestQueue.push( rqObj );

	return rqObj;
};



/*	Method: pax.cancel
	Cancel the pax call, and destroy request object
	
	Parameters:
		robj - Request object to cancel call from

	Example:
		(start code)
			<div id='pax.cancel.example1'></div>
			[:.
				var mycallBack = function( xml, txt, url ) {
					var response = pax.unJSON( txt );
					pax.$('pax.cancel.example1').innerHTML = "The name retreived from the server is: " + response.data[0].name;
				};
				var myRO = pax.post( '/pax/pax.post.example.php', pax.postString( { action: 'editAction', id: 2 } ), mycallBack, 'Post example', 'pax.cancel.example1' );
				
				window.setTimeout( function() { pax.cancel( myRO.ro ); pax.$('pax.cancel.example1').innerHTML = 'Cancelled request'; }, 1000 );
			:]
		(end)

		This example creates a call that takes 2000ms, then cancels the call after 1000ms
*/
pax.cancel = function( robj ) {
	var requestQueueItem = pax.getRequestQueueItem( robj );
	if( requestQueueItem ) {
		var timerInterval = pax.getRequestQueueItem( robj ).timerInterval;

		pax.hideStatus();
		pax.removeRequestQueueItem( robj );
		
		robj.abort();
		
		//	Destroy obj (so we don't leak memory)
		robj.onreadystatechange = function(){};
		if( timerInterval ) {
			clearInterval( timerInterval );
			timerInterval = null;
		}
	}
	robj = null;
};
